﻿/*
 * Copyright (c) 2024 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * -----------------------------------------------------------------------------
 *
 * $Date:       24. August 2024
 * $Revision:   V3.0
 *
 * Project:     USART Driver for STMicroelectronics STM32 devices
 *
 * -----------------------------------------------------------------------------
 */

/*! \page usart_stm32 USART

# Revision History

- Version 3.0
  - Initial release

# Requirements

This driver requires the STM32 device specific **HAL** and **STM32CubeMX** (CubeMX) initialization code generator.
The driver instance is mapped to hardware as shown in the table below:

  CMSIS Driver Instance | STM32 Hardware Resource
  :---------------------|:-----------------------
  Driver_USART1         | U(S)ART1
  Driver_USART2         | U(S)ART2
  Driver_USART3         | U(S)ART3
  Driver_USART4         | U(S)ART4
  Driver_USART5         | U(S)ART5
  Driver_USART6         | U(S)ART6
  Driver_USART7         | U(S)ART7
  Driver_USART8         | U(S)ART8
  Driver_USART9         | U(S)ART9
  Driver_USART10        | U(S)ART10
  Driver_USART11        | U(S)ART11
  Driver_USART12        | U(S)ART12
  Driver_USART13        | U(S)ART13
  Driver_USART14        | U(S)ART14
  Driver_USART15        | U(S)ART15
  Driver_USART21        | LPUART1
  Driver_USART22        | LPUART2
  Driver_USART23        | LPUART3

# Deviations

This driver has the following deviations from the CMSIS-Driver specification:

__Conceptual__ deviations:
  - CubeMX generated initialization code (function MX_UARTn_Init or MX_USARTn_UART_Init) already configures
    the peripheral. Power, clocks, pins, and interrupts are enabled after execution
    of initialization that executes in `main.c`.

__Functional__ deviations:
  - GetCapabilities:
    - depends on the code generated by CubeMX.
  - Initialize:
    - depends on the code generated by CubeMX.
    - disables Transmitter and Receiver.
    - does not initialize the pins.
  - Uninitialize:
    - does not de-initialize the pins.
  - PowerControl:
    - depends on the code generated by CubeMX.
    - low-power mode is not supported by HAL.
  - Send:
    - maximum number of data items supported with a single send request is 65535.
  - Receive:
    - maximum number of data items supported with a single receive request is 65535.
  - Control:
    - Tx break generation is not supported by HAL.
    - manual control of flow control lines is not supported by HAL.
  - GetStatus:
    - Rx break status is not supported by HAL (always 0).
    - Rx overflow cannot be detected until Receive is started.
  - SetModemControl:
    - not supported by HAL.
  - GetModemStatus:
    - not supported by HAL.
  - SignalEvent:
    - Rx break detection and signaling is not supported by HAL.
    - Rx overflow detection does not work until Receive is started.

# CubeMX Configuration

This driver requires the following configuration in CubeMX:

  - **clock**: **UART/USART** peripheral clock.
  - **peripheral**: **UART/USART** peripheral configured as **Asynchronous** with optional **Hardware Flow Control**
    and **Parameter Settings** configured as desired, except **Advanced Parameters: Fifo Mode**, if it exists, should be
    set to **Disable**.
  - **pins**: **UART/USART TX** and **UART/USART RX pins**, and optional **Hardware flow Control pins**.
  - **DMA**: optional **DMA** configuration for transfers.
  - **interrupts**: enabled **UART/USART interrupts** and **IRQ handlers** that **Call HAL handlers** and
    enabled **DMA interrupts** and **IRQ handlers** that **Call HAL handlers** if **DMA** is used.

> **Notes**
>
> - configuration information in the **MX_Device.h** file is based on CubeMX configuration.
> - **DMA** configuration can differ between devices series so configure DMA **as required by the used device**.
> - for **DMA** usage on devices with cache, ensure that data buffers for Send and Receive functions
>   are in **non-cacheable memory**, or ensure that memory for send is updated (**cache clean**) before Send function
>   is called and that memory containing received data is updated after the reception finishes (**cache invalidate**).
> - some DMA controllers can only access specific memories, so ensure that proper memory is used for the buffers
>   according to the DMA requirement.

## Example

### Pinout & Configuration tab

  1. In the **Pinout view** window click on a pin and select it's functionality:
       Pin      | Functionality
       :--------|:--------------------:
       PA11     | **USART1_CTS**
       PA12     | **USART1_RTS**
       PB14     | **USART1_TX**
       PB15     | **USART1_RX**
     \n

  2. Under **Categories**: **Connectivity** select **USART1**:

     __Mode__:
       - Mode: **Asynchronous**
       - Hardware Flow Control (RS232): **CTS/RTS**

     __Configuration__:
       - Parameter Settings: as desired, except **Advanced Parameters: Fifo Mode** which should be set to **Disable**
       - GPIO Settings:
           Pin Name | Signal on Pin | Pin Context..| GPIO output..| GPIO mode                     | GPIO Pull-up/Pull..| Maximum out..| Fast Mode | User Label
           :--------|:-------------:|:------------:|:------------:|:-----------------------------:|:------------------:|:------------:|:---------:|:----------:
           PA11     | USART1_CTS    | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| Low          | n/a       | .
           PA12     | USART1_RTS    | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| Low          | n/a       | .
           PB14     | USART1_TX     | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| Low          | n/a       | .
           PB15     | USART1_RX     | n/a          | n/a          | Alternate Function Push Pull  | No pull-up and no..| Low          | n/a       | .
         \n

  3. Under **Categories**: **System Core** select **DMA**
     (might be different on other device series, or for some peripherals might be BDMA):

     __Configuration__:
       - DMA1, DMA2:

         Click on **Add** button and add DMA Requests as in table below:
           DMA Request | Stream         | Direction            | Priority
           :----------:|:--------------:|:---------------------|:--------
           USART1_RX   | DMA1 Stream 0  | Peripheral to Memory | Low
           USART1_TX   | DMA1 Stream 1  | Memory to Peripheral | Low
         \n

  4. Under **Categories**: **System Core** select **NVIC**:

     __Configuration__:
       - NVIC:
           NVIC Interrupt Table              | Enabled     | Preemption Priority | Sub Priority
           :---------------------------------|:-----------:|:-------------------:|:------------:
           DMA1 stream0 global interrupt     |   checked   | 0                   | 0
           DMA1 stream1 global interrupt     |   checked   | 0                   | 0
           USART1 global interrupt           | **checked** | 0                   | 0
         \n

       - Code generation:
           Enabled interrupt table           | Select for..| Generate Enable in..| Generate IRQ h.. | Call HAL handler
           :---------------------------------|:-----------:|:-------------------:|:----------------:|:----------------:
           DMA1 stream0 global interrupt     | unchecked   | checked             | checked          | checked
           DMA1 stream1 global interrupt     | unchecked   | checked             | checked          | checked
           USART1 global interrupt           | unchecked   | checked             | checked          | checked
         \n

### Clock Configuration tab

  1. Configure **To USART1,6,9,10 (MHz)**: **100**

### Project Manager tab

  1. Under **Advanced Settings**:

     __Generated Function Calls__:
       Generate Code | Function Name               | Peripheral Inst..| Do not generate ..| Visibility (Static)
       :-------------|:---------------------------:|:----------------:|:-----------------:|:-------------------:
       checked       | MX_USART1_UART_Init         | USART1           | unchecked         | checked
*/

/*! \cond */

#include "USART_STM32.h"

#include "RTE_Components.h"
#include  CMSIS_device_header

#include <string.h>

// Driver Version **************************************************************
                                                //  CMSIS Driver API version           , Driver version
static  const ARM_DRIVER_VERSION driver_version = { ARM_DRIVER_VERSION_MAJOR_MINOR(2,4), ARM_DRIVER_VERSION_MAJOR_MINOR(3,0) };
// *****************************************************************************

// Compile-time configuration **************************************************

// Configuration depending on MX_Device.h

// Check if at least one peripheral instance is configured in STM32CubeMX
#if    (!defined(MX_UART1)  && \
        !defined(MX_UART2)  && \
        !defined(MX_UART3)  && \
        !defined(MX_UART4)  && \
        !defined(MX_UART5)  && \
        !defined(MX_UART6)  && \
        !defined(MX_UART7)  && \
        !defined(MX_UART8)  && \
        !defined(MX_UART9)  && \
        !defined(MX_UART10) && \
        !defined(MX_UART11) && \
        !defined(MX_UART12) && \
        !defined(MX_UART13) && \
        !defined(MX_UART14) && \
        !defined(MX_UART15) && \
        !defined(MX_UART21) && \
        !defined(MX_UART22) && \
        !defined(MX_UART23))
#error  USART driver requires at least one UART/USART peripheral configured in STM32CubeMX!

// Check if MX_Device.h version is as required (old version did not have all the necessary information)
#elif  (!defined(MX_DEVICE_VERSION) || (MX_DEVICE_VERSION < 0x01000000U))
#error  USART driver requires new MX_Device.h configuration, please regenerate MX_Device.h file!

#else
#define DRIVER_CONFIG_VALID     1
#endif

// *****************************************************************************

#ifdef  DRIVER_CONFIG_VALID     // Driver code is available only if configuration is valid

// Macros
// Macro for section for RW info
#ifdef  USART_SECTION_NAME
#define USARTn_SECTION_(name,n) __attribute__((section(name #n)))
#define USARTn_SECTION(n)       USARTn_SECTION_(USART_SECTION_NAME,n)
#else
#define USARTn_SECTION(n)
#endif

// Macro to create usart_ro_info and usart_rw_info (for U(S)ART instances)
#define INFO_DEFINE(n)                                                                                         \
extern  UART_HandleTypeDef      huart##n;                                                                      \
static        RW_Info_t         usart##n##_rw_info USARTn_SECTION(n);                                          \
static  const RO_Info_t         usart##n##_ro_info = { &huart##n,                                              \
                                                       &usart##n##_rw_info                                     \
                                                     };

// Macro to create usart_ro_info and usart_rw_info (for LPUART instances)
#define LP_INFO_DEFINE(n,lp_n)                                                                                 \
extern  UART_HandleTypeDef      hlpuart##lp_n;                                                                 \
static        RW_Info_t         usart##n##_rw_info USARTn_SECTION(n);                                          \
static  const RO_Info_t         usart##n##_ro_info = { &hlpuart##lp_n,                                         \
                                                       &usart##n##_rw_info                                     \
                                                     };

// Macro for declaring functions (for instances)
#define FUNCS_DECLARE(n)                                                                                       \
static  ARM_USART_CAPABILITIES  USART##n##_GetCapabilities (void);                                             \
static  int32_t                 USART##n##_Initialize      (ARM_USART_SignalEvent_t cb_event);                 \
static  int32_t                 USART##n##_Uninitialize    (void);                                             \
static  int32_t                 USART##n##_PowerControl    (ARM_POWER_STATE state);                            \
static  int32_t                 USART##n##_Send            (const void *data, uint32_t num);                   \
static  int32_t                 USART##n##_Receive         (void *data, uint32_t num);                         \
static  int32_t                 USART##n##_Transfer        (const void *data_out, void *data_in, uint32_t num);\
static  uint32_t                USART##n##_GetTxCount      (void);                                             \
static  uint32_t                USART##n##_GetRxCount      (void);                                             \
static  int32_t                 USART##n##_Control         (uint32_t control, uint32_t arg);                   \
static  ARM_USART_STATUS        USART##n##_GetStatus       (void);

// Macro for defining functions (for instances)
#define FUNCS_DEFINE(n)                                                                                                                                                                         \
static  ARM_USART_CAPABILITIES  USART##n##_GetCapabilities (void)                                              { return USARTn_GetCapabilities (&usart##n##_ro_info); }                         \
static  int32_t                 USART##n##_Initialize      (ARM_USART_SignalEvent_t cb_event)                  { return USARTn_Initialize      (&usart##n##_ro_info, cb_event); }               \
static  int32_t                 USART##n##_Uninitialize    (void)                                              { return USARTn_Uninitialize    (&usart##n##_ro_info); }                         \
static  int32_t                 USART##n##_PowerControl    (ARM_POWER_STATE state)                             { return USARTn_PowerControl    (&usart##n##_ro_info, state); }                  \
static  int32_t                 USART##n##_Send            (const void *data, uint32_t num)                    { return USARTn_Send            (&usart##n##_ro_info, data, num); }              \
static  int32_t                 USART##n##_Receive         (void *data, uint32_t num)                          { return USARTn_Receive         (&usart##n##_ro_info, data, num); }              \
static  int32_t                 USART##n##_Transfer        (const void *data_out, void *data_in, uint32_t num) { return USARTn_Transfer        (&usart##n##_ro_info, data_out, data_in, num); } \
static  uint32_t                USART##n##_GetTxCount      (void)                                              { return USARTn_GetTxCount      (&usart##n##_ro_info); }                         \
static  uint32_t                USART##n##_GetRxCount      (void)                                              { return USARTn_GetRxCount      (&usart##n##_ro_info); }                         \
static  int32_t                 USART##n##_Control         (uint32_t control, uint32_t arg)                    { return USARTn_Control         (&usart##n##_ro_info, control, arg); }           \
static  ARM_USART_STATUS        USART##n##_GetStatus       (void)                                              { return USARTn_GetStatus       (&usart##n##_ro_info); }

// Macro for defining driver structures (for instances)
#define USART_DRIVER(n)                 \
ARM_DRIVER_USART Driver_USART##n = {    \
  USART_GetVersion,                     \
  USART##n##_GetCapabilities,           \
  USART##n##_Initialize,                \
  USART##n##_Uninitialize,              \
  USART##n##_PowerControl,              \
  USART##n##_Send,                      \
  USART##n##_Receive,                   \
  USART##n##_Transfer,                  \
  USART##n##_GetTxCount,                \
  USART##n##_GetRxCount,                \
  USART##n##_Control,                   \
  USART##n##_GetStatus,                 \
  USART_SetModemControl,                \
  USART_GetModemStatus                  \
};

// Driver status
typedef struct {
  uint8_t                       initialized  : 1;       // Initialized status: 0 - not initialized, 1 - initialized
  uint8_t                       powered      : 1;       // Power status:       0 - not powered,     1 - powered
  uint8_t                       configured   : 1;       // Configured status:  0 - not configured,  1 - configured
  uint8_t                       reserved     : 5;       // Reserved (for padding)
} DriverStatus_t;

// Instance run-time information (RW)
typedef struct {
  ARM_USART_SignalEvent_t       cb_event;               // Event callback
  DriverStatus_t                drv_status;             // Driver status
  volatile uint8_t              rx_overflow;            // Receive data overflow detected (cleared on start of next receive operation)
  volatile uint8_t              rx_framing_error;       // Framing error detected on receive (cleared on start of next receive operation)
  volatile uint8_t              rx_parity_error;        // Parity error detected on receive (cleared on start of next receive operation)
} RW_Info_t;

// Instance compile-time information (RO)
// also contains pointer to run-time information
typedef struct {
  UART_HandleTypeDef           *ptr_huart;              // Pointer to UART handle
  RW_Info_t                    *ptr_rw_info;            // Pointer to run-time information (RW)
} RO_Info_t;

// Information definitions (for instances)
#ifdef MX_UART1
INFO_DEFINE(1)
#endif
#ifdef MX_UART2
INFO_DEFINE(2)
#endif
#ifdef MX_UART3
INFO_DEFINE(3)
#endif
#ifdef MX_UART4
INFO_DEFINE(4)
#endif
#ifdef MX_UART5
INFO_DEFINE(5)
#endif
#ifdef MX_UART6
INFO_DEFINE(6)
#endif
#ifdef MX_UART7
INFO_DEFINE(7)
#endif
#ifdef MX_UART8
INFO_DEFINE(8)
#endif
#ifdef MX_UART9
INFO_DEFINE(9)
#endif
#ifdef MX_UART10
INFO_DEFINE(10)
#endif
#ifdef MX_UART11
INFO_DEFINE(11)
#endif
#ifdef MX_UART12
INFO_DEFINE(12)
#endif
#ifdef MX_UART13
INFO_DEFINE(13)
#endif
#ifdef MX_UART14
INFO_DEFINE(14)
#endif
#ifdef MX_UART15
INFO_DEFINE(15)
#endif
#ifdef MX_UART21
LP_INFO_DEFINE(21,1)
#endif
#ifdef MX_UART22
LP_INFO_DEFINE(22,2)
#endif
#ifdef MX_UART23
LP_INFO_DEFINE(23,3)
#endif

// List of available USART instance infos
static const RO_Info_t * const usart_ro_info_list[] = {
#ifdef MX_UART1
  &usart1_ro_info,
#endif
#ifdef MX_UART2
  &usart2_ro_info,
#endif
#ifdef MX_UART3
  &usart3_ro_info,
#endif
#ifdef MX_UART4
  &usart4_ro_info,
#endif
#ifdef MX_UART5
  &usart5_ro_info,
#endif
#ifdef MX_UART6
  &usart6_ro_info,
#endif
#ifdef MX_UART7
  &usart7_ro_info,
#endif
#ifdef MX_UART8
  &usart8_ro_info,
#endif
#ifdef MX_UART9
  &usart9_ro_info,
#endif
#ifdef MX_UART10
  &usart10_ro_info,
#endif
#ifdef MX_UART11
  &usart11_ro_info,
#endif
#ifdef MX_UART12
  &usart12_ro_info,
#endif
#ifdef MX_UART13
  &usart13_ro_info,
#endif
#ifdef MX_UART14
  &usart14_ro_info,
#endif
#ifdef MX_UART15
  &usart15_ro_info,
#endif
#ifdef MX_UART21
  &usart21_ro_info,
#endif
#ifdef MX_UART22
  &usart22_ro_info,
#endif
#ifdef MX_UART23
  &usart23_ro_info,
#endif
  NULL
};

// Local functions prototypes
static const RO_Info_t         *USART_GetInfo          (const UART_HandleTypeDef * const huart);
static ARM_DRIVER_VERSION       USART_GetVersion       (void);
static ARM_USART_CAPABILITIES   USARTn_GetCapabilities (const RO_Info_t * const ptr_ro_info);
static int32_t                  USARTn_Initialize      (const RO_Info_t * const ptr_ro_info, ARM_USART_SignalEvent_t cb_event);
static int32_t                  USARTn_Uninitialize    (const RO_Info_t * const ptr_ro_info);
static int32_t                  USARTn_PowerControl    (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state);
static int32_t                  USARTn_Send            (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num);
static int32_t                  USARTn_Receive         (const RO_Info_t * const ptr_ro_info,       void *data, uint32_t num);
static int32_t                  USARTn_Transfer        (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num);
static uint32_t                 USARTn_GetTxCount      (const RO_Info_t * const ptr_ro_info);
static uint32_t                 USARTn_GetRxCount      (const RO_Info_t * const ptr_ro_info);
static int32_t                  USARTn_Control         (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg);
static ARM_USART_STATUS         USARTn_GetStatus       (const RO_Info_t * const ptr_ro_info);
static int32_t                  USART_SetModemControl  (ARM_USART_MODEM_CONTROL control);
static ARM_USART_MODEM_STATUS   USART_GetModemStatus   (void);

// Local driver functions declarations (for instances)
#ifdef MX_UART1
FUNCS_DECLARE(1)
#endif
#ifdef MX_UART2
FUNCS_DECLARE(2)
#endif
#ifdef MX_UART3
FUNCS_DECLARE(3)
#endif
#ifdef MX_UART4
FUNCS_DECLARE(4)
#endif
#ifdef MX_UART5
FUNCS_DECLARE(5)
#endif
#ifdef MX_UART6
FUNCS_DECLARE(6)
#endif
#ifdef MX_UART7
FUNCS_DECLARE(7)
#endif
#ifdef MX_UART8
FUNCS_DECLARE(8)
#endif
#ifdef MX_UART9
FUNCS_DECLARE(9)
#endif
#ifdef MX_UART10
FUNCS_DECLARE(10)
#endif
#ifdef MX_UART11
FUNCS_DECLARE(11)
#endif
#ifdef MX_UART12
FUNCS_DECLARE(12)
#endif
#ifdef MX_UART13
FUNCS_DECLARE(13)
#endif
#ifdef MX_UART14
FUNCS_DECLARE(14)
#endif
#ifdef MX_UART15
FUNCS_DECLARE(15)
#endif
#ifdef MX_UART21
FUNCS_DECLARE(21)
#endif
#ifdef MX_UART22
FUNCS_DECLARE(22)
#endif
#ifdef MX_UART23
FUNCS_DECLARE(23)
#endif

// Auxiliary functions

/**
  \fn          RO_Info_t *USART_GetInfo (const UART_HandleTypeDef * const huart)
  \brief       Get pointer to RO_Info_t structure corresponding to specified huart.
  \param[in]   huart    Pointer to UART handle structure (UART_HandleTypeDef)
  \return      pointer to USART RO info structure (RO_Info_t)
*/
static const RO_Info_t *USART_GetInfo (const UART_HandleTypeDef * const huart) {
  const RO_Info_t *ptr_ro_info;
        uint8_t    i;

  ptr_ro_info = NULL;
  i           = 0U;

  // Find UART which uses same huart handle as parameter ptr_huart
  for (i = 0U; i < (sizeof(usart_ro_info_list)/sizeof(RO_Info_t *)); i++) {
    if (usart_ro_info_list[i] != NULL) {
      if (usart_ro_info_list[i]->ptr_huart == huart) {
        ptr_ro_info = usart_ro_info_list[i];
        break;
      }
    }
  }

  return ptr_ro_info;
}

// Driver functions ************************************************************

/**
  \fn          ARM_DRIVER_VERSION USART_GetVersion (void)
  \brief       Get driver version.
  \return      \ref ARM_DRIVER_VERSION
*/
static ARM_DRIVER_VERSION USART_GetVersion (void) {
  return driver_version;
}

/**
  \fn          ARM_USART_CAPABILITIES USARTn_GetCapabilities (const RO_Info_t * const ptr_ro_info)
  \brief       Get driver capabilities.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \return      \ref ARM_USART_CAPABILITIES
*/
static ARM_USART_CAPABILITIES USARTn_GetCapabilities (const RO_Info_t * const ptr_ro_info) {
  ARM_USART_CAPABILITIES driver_capabilities;

  // Clear capabilities structure
  memset(&driver_capabilities, 0, sizeof(ARM_USART_CAPABILITIES));

  // Load capability fields different than 0
  driver_capabilities.asynchronous = 1U;
  switch (ptr_ro_info->ptr_huart->Init.HwFlowCtl) {
    case UART_HWCONTROL_RTS:
      driver_capabilities.flow_control_rts = 1U;
      driver_capabilities.flow_control_cts = 0U;
      break;

    case UART_HWCONTROL_CTS:
      driver_capabilities.flow_control_rts = 0U;
      driver_capabilities.flow_control_cts = 1U;
      break;

    case UART_HWCONTROL_RTS_CTS:
      driver_capabilities.flow_control_rts = 1U;
      driver_capabilities.flow_control_cts = 1U;
      break;

    case UART_HWCONTROL_NONE:
    default:
      driver_capabilities.flow_control_rts = 0U;
      driver_capabilities.flow_control_cts = 0U;
      break;
  }
  driver_capabilities.event_tx_complete = 1U;

  return driver_capabilities;
}

/**
  \fn          int32_t USARTn_Initialize (const RO_Info_t * const ptr_ro_info, ARM_USART_SignalEvent_t cb_event)
  \brief       Initialize USART Interface.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[in]   cb_event        Pointer to \ref ARM_USART_SignalEvent
  \return      \ref execution_status
*/
static int32_t USARTn_Initialize (const RO_Info_t * const ptr_ro_info, ARM_USART_SignalEvent_t cb_event) {

  // Clear run-time info
  memset((void *)ptr_ro_info->ptr_rw_info, 0, sizeof(RW_Info_t));

  // Register callback function
  ptr_ro_info->ptr_rw_info->cb_event = cb_event;

  // Set driver status to initialized
  ptr_ro_info->ptr_rw_info->drv_status.initialized = 1U;

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t USARTn_Uninitialize (const RO_Info_t * const ptr_ro_info)
  \brief       De-initialize USART Interface.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \return      \ref execution_status
*/
static int32_t USARTn_Uninitialize (const RO_Info_t * const ptr_ro_info) {

  if (ptr_ro_info->ptr_rw_info->drv_status.powered != 0U) {
    // If peripheral is powered, power off the peripheral
    (void)USARTn_PowerControl(ptr_ro_info, ARM_POWER_OFF);
  }

  // Clear run-time info
  memset((void *)ptr_ro_info->ptr_rw_info, 0, sizeof(RW_Info_t));

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t USARTn_PowerControl (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state)
  \brief       Control USART Interface Power.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[in]   state           Power state
  \return      \ref execution_status
*/
static int32_t USARTn_PowerControl (const RO_Info_t * const ptr_ro_info, ARM_POWER_STATE state) {

  switch (state) {
    case ARM_POWER_FULL:
      if (ptr_ro_info->ptr_rw_info->drv_status.initialized == 0U) {
        return ARM_DRIVER_ERROR;
      }

      // Clear communication error status
      ptr_ro_info->ptr_rw_info->rx_overflow      = 0U;
      ptr_ro_info->ptr_rw_info->rx_framing_error = 0U;
      ptr_ro_info->ptr_rw_info->rx_parity_error  = 0U;

      // Initialize pins, clocks, interrupts and peripheral
      if (HAL_UART_Init(ptr_ro_info->ptr_huart) != HAL_OK) {
        return ARM_DRIVER_ERROR;
      }

      // Set driver status to powered
      ptr_ro_info->ptr_rw_info->drv_status.powered = 1U;
      break;

    case ARM_POWER_OFF:

      // If send operation is in progress, abort it
      if (USARTn_GetStatus(ptr_ro_info).tx_busy != 0U) {
        (void)USARTn_Control(ptr_ro_info, ARM_USART_ABORT_SEND, 0U);
      }
      // If receive operation is in progress, abort it
      if (USARTn_GetStatus(ptr_ro_info).rx_busy != 0U) {
        (void)USARTn_Control(ptr_ro_info, ARM_USART_ABORT_RECEIVE, 0U);
      }

      // De-initialize pins, clocks, interrupts and peripheral
      (void)HAL_UART_DeInit(ptr_ro_info->ptr_huart);

      // Set driver status to not powered
      ptr_ro_info->ptr_rw_info->drv_status.powered = 0U;

      // Clear communication error status
      ptr_ro_info->ptr_rw_info->rx_overflow      = 0U;
      ptr_ro_info->ptr_rw_info->rx_framing_error = 0U;
      ptr_ro_info->ptr_rw_info->rx_parity_error  = 0U;
      break;

    case ARM_POWER_LOW:
      return ARM_DRIVER_ERROR_UNSUPPORTED;

    default:
      return ARM_DRIVER_ERROR_PARAMETER;
  }

  return ARM_DRIVER_OK;
}

/**
  \fn          int32_t USARTn_Send (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num)
  \brief       Start sending data to USART transmitter.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[in]   data            Pointer to buffer with data to send to USART transmitter
  \param[in]   num             Number of data items to send
  \return      \ref execution_status
*/
static int32_t USARTn_Send (const RO_Info_t * const ptr_ro_info, const void *data, uint32_t num) {
  HAL_StatusTypeDef send_status;
  int32_t           ret;

  if ((data == NULL) || (num == 0U) || (num > (uint32_t)UINT16_MAX)) {
    // If any parameter is invalid
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Start the send
  if (ptr_ro_info->ptr_huart->hdmatx != NULL) { // If DMA is used for Tx
    send_status = HAL_UART_Transmit_DMA(ptr_ro_info->ptr_huart, (const uint8_t *)data, (uint16_t)num);
  } else {                                      // If DMA is not configured (IRQ mode)
    send_status = HAL_UART_Transmit_IT (ptr_ro_info->ptr_huart, (const uint8_t *)data, (uint16_t)num);
  }

  // Convert HAL status code to CMSIS-Driver status code
  switch (send_status) {
    case HAL_ERROR:
      ret = ARM_DRIVER_ERROR;
      break;

    case HAL_BUSY:
      ret = ARM_DRIVER_ERROR_BUSY;
      break;

    case HAL_OK:
      ret = ARM_DRIVER_OK;
      break;

    case HAL_TIMEOUT:
    default:
      ret = ARM_DRIVER_ERROR;
      break;
  }

  return ret;
}

/**
  \fn          int32_t USARTn_Receive (const RO_Info_t * const ptr_ro_info, void *data, uint32_t num)
  \brief       Start receiving data from USART receiver.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[out]  data            Pointer to buffer for data to receive from USART receiver
  \param[in]   num             Number of data items to receive
  \return      \ref execution_status
*/
static int32_t USARTn_Receive (const RO_Info_t * const ptr_ro_info, void *data, uint32_t num) {
  HAL_StatusTypeDef receive_status;
  int32_t           ret;

  if ((data == NULL) || (num == 0U) || (num > (uint32_t)UINT16_MAX)) {
    // If any parameter is invalid
    return ARM_DRIVER_ERROR_PARAMETER;
  }

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Clear communication error status
  ptr_ro_info->ptr_rw_info->rx_overflow      = 0U;
  ptr_ro_info->ptr_rw_info->rx_framing_error = 0U;
  ptr_ro_info->ptr_rw_info->rx_parity_error  = 0U;

  // Start the reception
  if (ptr_ro_info->ptr_huart->hdmarx != NULL) { // If DMA is used for Rx
    receive_status = HAL_UART_Receive_DMA(ptr_ro_info->ptr_huart, (uint8_t *)data, (uint16_t)num);
  } else {                                      // If DMA is not configured (IRQ mode)
    receive_status = HAL_UART_Receive_IT (ptr_ro_info->ptr_huart, (uint8_t *)data, (uint16_t)num);
  }

  // Convert HAL status code to CMSIS-Driver status code
  switch (receive_status) {
    case HAL_ERROR:
      ret = ARM_DRIVER_ERROR;
      break;

    case HAL_BUSY:
      ret = ARM_DRIVER_ERROR_BUSY;
      break;

    case HAL_OK:
      ret = ARM_DRIVER_OK;
      break;

    case HAL_TIMEOUT:
    default:
      ret = ARM_DRIVER_ERROR;
      break;
  }

  return ret;
}

/**
  \fn          int32_t USARTn_Transfer (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num)
  \brief       Start sending/receiving data to/from USART transmitter/receiver.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[in]   data_out        Pointer to buffer with data to send to USART transmitter
  \param[out]  data_in         Pointer to buffer for data to receive from USART receiver
  \param[in]   num             Number of data items to transfer
  \return      \ref execution_status
*/
static int32_t USARTn_Transfer (const RO_Info_t * const ptr_ro_info, const void *data_out, void *data_in, uint32_t num) {
  (void)data_out;
  (void)data_in;
  (void)num;
  (void)ptr_ro_info;

  if (ptr_ro_info->ptr_rw_info->drv_status.configured == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Supported only in Synchronous mode
  return ARM_DRIVER_ERROR_UNSUPPORTED;
}

/**
  \fn          uint32_t USARTn_GetTxCount (const RO_Info_t * const ptr_ro_info)
  \brief       Get transmitted data count.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \return      number of data items transmitted
*/
static uint32_t USARTn_GetTxCount (const RO_Info_t * const ptr_ro_info) {
  uint32_t cnt;
  uint32_t cnt_xferred;

  if (ptr_ro_info->ptr_rw_info->drv_status.powered == 0U) {
    return 0U;
  }

  if (ptr_ro_info->ptr_huart->hdmatx != NULL) { // If DMA is used for Tx
    cnt_xferred = __HAL_DMA_GET_COUNTER(ptr_ro_info->ptr_huart->hdmatx);
  } else {
    cnt_xferred = ptr_ro_info->ptr_huart->TxXferCount;
  }

  cnt = (uint32_t)ptr_ro_info->ptr_huart->TxXferSize - cnt_xferred;

  return cnt;
}

/**
  \fn          uint32_t USARTn_GetRxCount (const RO_Info_t * const ptr_ro_info)
  \brief       Get received data count.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \return      number of data items received
*/
static uint32_t USARTn_GetRxCount (const RO_Info_t * const ptr_ro_info) {
  uint32_t cnt;
  uint32_t cnt_xferred;

  if (ptr_ro_info->ptr_rw_info->drv_status.powered == 0U) {
    return 0U;
  }

  if (ptr_ro_info->ptr_huart->hdmarx != NULL) { // If DMA is used for Rx
    cnt_xferred = __HAL_DMA_GET_COUNTER(ptr_ro_info->ptr_huart->hdmarx);
  } else {
    cnt_xferred = ptr_ro_info->ptr_huart->RxXferCount;
  }

  cnt = (uint32_t)ptr_ro_info->ptr_huart->RxXferSize - cnt_xferred;

  return cnt;
}

/**
  \fn          int32_t USARTn_Control (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg)
  \brief       Control USART Interface.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \param[in]   control         Operation
  \param[in]   arg             Argument of operation (optional)
  \return      common \ref execution_status and driver specific \ref usart_execution_status
*/
static int32_t USARTn_Control (const RO_Info_t * const ptr_ro_info, uint32_t control, uint32_t arg) {
  ARM_USART_STATUS status;
  uint8_t          parity_bits;
  uint8_t          data_bits;

  if (ptr_ro_info->ptr_rw_info->drv_status.powered == 0U) {
    return ARM_DRIVER_ERROR;
  }

  // Special handling for Abort Send command
  if ((control & ARM_USART_CONTROL_Msk) == ARM_USART_ABORT_SEND) {
    if (HAL_UART_AbortTransmit(ptr_ro_info->ptr_huart) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
    if (ptr_ro_info->ptr_huart->hdmatx == NULL) {
      // If DMA is not used for Tx, clear TxXferSize for GetTxCount to work properly
      ptr_ro_info->ptr_huart->TxXferSize = 0U;
    }
    return ARM_DRIVER_OK;
  }

  // Special handling for Abort Receive command
  if ((control & ARM_USART_CONTROL_Msk) == ARM_USART_ABORT_RECEIVE) {
    if (HAL_UART_AbortReceive(ptr_ro_info->ptr_huart) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
    if (ptr_ro_info->ptr_huart->hdmarx == NULL) {
      // If DMA is not used for Rx, clear RxXferSize for GetRxCount to work properly
      ptr_ro_info->ptr_huart->RxXferSize = 0U;
    }
    return ARM_DRIVER_OK;
  }

  // Check if peripheral is busy
  status = USARTn_GetStatus(ptr_ro_info);
  if ((status.tx_busy != 0U) || (status.rx_busy != 0U)) {
    return ARM_DRIVER_ERROR_BUSY;
  }

  switch (control & ARM_USART_CONTROL_Msk) {    // --- Control: Mode and Miscellaneous
                                                // --- Control Mode
    case ARM_USART_MODE_ASYNCHRONOUS:           // Mode: Asynchronous
      ptr_ro_info->ptr_rw_info->drv_status.configured = 0U;
      break;                                    // Continue configuring parameters after this switch block

    case ARM_USART_MODE_SYNCHRONOUS_MASTER:     // Mode: Synchronous Master
    case ARM_USART_MODE_SYNCHRONOUS_SLAVE:      // Mode: Synchronous Slave
    case ARM_USART_MODE_SINGLE_WIRE:            // Mode: Single-wire
    case ARM_USART_MODE_IRDA:                   // Mode: IrDA
    case ARM_USART_MODE_SMART_CARD:             // Mode: Smart Card
      return ARM_USART_ERROR_MODE;

                                                // --- Control Miscellaneous
    case ARM_USART_CONTROL_TX:                  // Transmitter; arg: 0=disabled, 1=enabled
      if (arg != 0U) {
        // Enable transmitter
        ptr_ro_info->ptr_huart->Init.Mode |=  UART_MODE_TX;
      } else {
        // Disable transmitter
        ptr_ro_info->ptr_huart->Init.Mode &= ~UART_MODE_TX;
      }
      if (HAL_UART_Init(ptr_ro_info->ptr_huart) != HAL_OK) {
        return ARM_DRIVER_ERROR;
      }
      return ARM_DRIVER_OK;

    case ARM_USART_CONTROL_RX:                  // Receiver; arg: 0=disabled, 1=enabled
      if (arg != 0U) {
        // Enable receiver
        ptr_ro_info->ptr_huart->Init.Mode |=  UART_MODE_RX;
      } else {
        // Disable receiver
        ptr_ro_info->ptr_huart->Init.Mode &= ~UART_MODE_RX;
      }
      if (HAL_UART_Init(ptr_ro_info->ptr_huart) != HAL_OK) {
        return ARM_DRIVER_ERROR;
      }
      return ARM_DRIVER_OK;

    case ARM_USART_SET_DEFAULT_TX_VALUE:        // Set default Transmit value
    case ARM_USART_SET_IRDA_PULSE:              // Set IrDA Pulse in ns
    case ARM_USART_SET_SMART_CARD_GUARD_TIME:   // Set Smart Card Guard Time
    case ARM_USART_SET_SMART_CARD_CLOCK:        // Set Smart Card Clock in Hz
    case ARM_USART_CONTROL_SMART_CARD_NACK:     // Smart Card NACK generation
    case ARM_USART_CONTROL_BREAK:               // Continuous Break transmission
    case ARM_USART_ABORT_TRANSFER:              // Abort ARM_USART_Transfer
      return ARM_DRIVER_ERROR_UNSUPPORTED;

    default:                                    // Unknown Control
      return ARM_DRIVER_ERROR_PARAMETER;
  }

  // Configure all other parameters if command was to set Asynchronous mode (ARM_USART_MODE_ASYNCHRONOUS)

  // Determine number of parity bits used
  parity_bits = 0U;
  if ((control & ARM_USART_PARITY_Msk) != ARM_USART_PARITY_NONE) {
    parity_bits = 1U;
  }

  data_bits = 0U;
  switch (control & ARM_USART_DATA_BITS_Msk) {  // --- Mode Parameters: Data Bits
    case ARM_USART_DATA_BITS_6:                 // Data bits: 6
#ifdef UART_WORDLENGTH_7B
      if (parity_bits == 1U) {
        data_bits = 6;
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_7B;
      } else {
        return ARM_USART_ERROR_DATA_BITS;
      }
#else
      return ARM_USART_ERROR_DATA_BITS;
#endif
      break;

    case ARM_USART_DATA_BITS_7:                 // Data bits: 7
      if (parity_bits == 1U) {
        data_bits = 7;
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_8B;
      } else {
#ifdef UART_WORDLENGTH_7B
        data_bits = 7;
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_7B;
#else
        return ARM_USART_ERROR_DATA_BITS;
#endif
      }
      break;

    case ARM_USART_DATA_BITS_8:                 // Data bits: 8
      data_bits = 8;
      if (parity_bits == 1U) {
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_9B;
      } else {
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_8B;
      }
      break;

    case ARM_USART_DATA_BITS_9:                 // Data bits: 9
      if (parity_bits == 0U) {
        data_bits = 9;
        ptr_ro_info->ptr_huart->Init.WordLength = UART_WORDLENGTH_9B;
      } else {
        return ARM_USART_ERROR_DATA_BITS;
      }
      break;

    default:
      return ARM_USART_ERROR_DATA_BITS;
  }

  switch (control & ARM_USART_PARITY_Msk) {     // --- Mode Parameters: Parity
    case ARM_USART_PARITY_NONE:                 // Parity: none
      ptr_ro_info->ptr_huart->Init.Parity = UART_PARITY_NONE;
      break;

    case ARM_USART_PARITY_EVEN:                 // Parity: even
      ptr_ro_info->ptr_huart->Init.Parity = UART_PARITY_EVEN;
      break;

    case ARM_USART_PARITY_ODD:                  // Parity: odd
      ptr_ro_info->ptr_huart->Init.Parity = UART_PARITY_ODD;
      break;

    default:
      return ARM_USART_ERROR_PARITY;
  }

  switch (control & ARM_USART_STOP_BITS_Msk) {  // --- Mode Parameters: Stop Bits
    case ARM_USART_STOP_BITS_1:                 // Stop Bits: 1
      ptr_ro_info->ptr_huart->Init.StopBits = UART_STOPBITS_1;
      break;

    case ARM_USART_STOP_BITS_2:                 // Stop Bits: 2
      ptr_ro_info->ptr_huart->Init.StopBits = UART_STOPBITS_2;
      break;

#ifdef UART_STOPBITS_1_5
    case ARM_USART_STOP_BITS_1_5:               // Stop Bits: 1.5
      ptr_ro_info->ptr_huart->Init.StopBits = UART_STOPBITS_1_5;
      break;
#endif

#ifdef UART_STOPBITS_0_5
    case ARM_USART_STOP_BITS_0_5:               // Stop Bits: 0.5
      ptr_ro_info->ptr_huart->Init.StopBits = UART_STOPBITS_0_5;
      break;
#endif

    default:
      return ARM_USART_ERROR_STOP_BITS;
  }

  switch (control & ARM_USART_FLOW_CONTROL_Msk) { // --- Mode Parameters: Flow Control
    case ARM_USART_FLOW_CONTROL_NONE:             // Flow Control: none
      ptr_ro_info->ptr_huart->Init.HwFlowCtl = UART_HWCONTROL_NONE;
      break;

    case ARM_USART_FLOW_CONTROL_RTS:              // Flow Control: RTS
      ptr_ro_info->ptr_huart->Init.HwFlowCtl = UART_HWCONTROL_RTS;
      break;

    case ARM_USART_FLOW_CONTROL_CTS:              // Flow Control: CTS
      ptr_ro_info->ptr_huart->Init.HwFlowCtl = UART_HWCONTROL_CTS;
      break;

    case ARM_USART_FLOW_CONTROL_RTS_CTS:          // Flow Control: RTS/CTS
      ptr_ro_info->ptr_huart->Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
      break;

    default:
      return ARM_USART_ERROR_FLOW_CONTROL;
  }

  // Configure baudrate
  ptr_ro_info->ptr_huart->Init.BaudRate = arg;

  if (ptr_ro_info->ptr_huart->hdmarx != NULL) { // If DMA is used for Rx
    if (data_bits == 9U) {
#if defined(DMA_MDATAALIGN_HALFWORD)
      ptr_ro_info->ptr_huart->hdmarx->Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
      ptr_ro_info->ptr_huart->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
#else
      ptr_ro_info->ptr_huart->hdmarx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_HALFWORD;
      ptr_ro_info->ptr_huart->hdmarx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_HALFWORD;
#endif
    } else {
#if defined(DMA_MDATAALIGN_BYTE)
      ptr_ro_info->ptr_huart->hdmarx->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      ptr_ro_info->ptr_huart->hdmarx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
#else
      ptr_ro_info->ptr_huart->hdmarx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_BYTE;
      ptr_ro_info->ptr_huart->hdmarx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_BYTE;
#endif
    }

    // Reconfigure DMA for Rx
    if (HAL_DMA_Init(ptr_ro_info->ptr_huart->hdmarx) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
  }

  // Reconfigure DMA
  if (ptr_ro_info->ptr_huart->hdmatx != NULL) { // If DMA is used for Tx
    if (data_bits == 9U) {
#if defined(DMA_MDATAALIGN_HALFWORD)
      ptr_ro_info->ptr_huart->hdmatx->Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;
      ptr_ro_info->ptr_huart->hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
#else
      ptr_ro_info->ptr_huart->hdmatx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_HALFWORD;
      ptr_ro_info->ptr_huart->hdmatx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_HALFWORD;
#endif
    } else {
#if defined(DMA_MDATAALIGN_BYTE)
      ptr_ro_info->ptr_huart->hdmatx->Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
      ptr_ro_info->ptr_huart->hdmatx->Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
#else
      ptr_ro_info->ptr_huart->hdmatx->Init.SrcDataWidth        = DMA_SRC_DATAWIDTH_BYTE;
      ptr_ro_info->ptr_huart->hdmatx->Init.DestDataWidth       = DMA_DEST_DATAWIDTH_BYTE;
#endif
    }

    // Reconfigure DMA for Tx
    if (HAL_DMA_Init(ptr_ro_info->ptr_huart->hdmatx) != HAL_OK) {
      return ARM_DRIVER_ERROR;
    }
  }

  // Reconfigure USART
  if (HAL_UART_Init(ptr_ro_info->ptr_huart) != HAL_OK) {
    return ARM_DRIVER_ERROR;
  }

  // Set driver status to configured
  ptr_ro_info->ptr_rw_info->drv_status.configured = 1U;

  return ARM_DRIVER_OK;
}

/**
  \fn          ARM_USART_STATUS USARTn_GetStatus (const RO_Info_t * const ptr_ro_info)
  \brief       Get USART status.
  \param[in]   ptr_ro_info     Pointer to USART RO info structure (RO_Info_t)
  \return      USART status \ref ARM_USART_STATUS
*/
static ARM_USART_STATUS USARTn_GetStatus (const RO_Info_t * const ptr_ro_info) {
  ARM_USART_STATUS status;

  // Clear status structure
  memset(&status, 0, sizeof(ARM_USART_STATUS));

  // Process HAL state
  switch (HAL_UART_GetState(ptr_ro_info->ptr_huart)) {
    case HAL_UART_STATE_BUSY:           // An internal process is ongoing
    case HAL_UART_STATE_BUSY_TX_RX:     // Data Transmission and Reception process is ongoing
      status.tx_busy = 1U;
      status.rx_busy = 1U;
      break;

    case HAL_UART_STATE_BUSY_TX:        // Data Transmission process is ongoing
      status.tx_busy = 1U;
      break;

    case HAL_UART_STATE_BUSY_RX:        // Data Reception process is ongoing
      status.rx_busy = 1U;
      break;

    case HAL_UART_STATE_RESET:          // Peripheral is not initialized
    case HAL_UART_STATE_READY:          // Peripheral Initialized and ready for use
    case HAL_UART_STATE_TIMEOUT:        // Timeout state
    case HAL_UART_STATE_ERROR:          // Error
    default:
      // Not busy related
      break;
  }

  // Process additionally handled communication information
  if (ptr_ro_info->ptr_rw_info->rx_overflow != 0U) {
    status.rx_overflow = 1U;
  }
  if (ptr_ro_info->ptr_rw_info->rx_framing_error != 0U) {
    status.rx_framing_error = 1U;
  }
  if (ptr_ro_info->ptr_rw_info->rx_parity_error != 0U) {
    status.rx_parity_error = 1U;
  }

  return status;
}

/**
  \fn          int32_t USART_SetModemControl (ARM_USART_MODEM_CONTROL control)
  \brief       Set USART Modem Control line state.
  \param[in]   control  \ref ARM_USART_MODEM_CONTROL
  \return      \ref execution_status
*/
static int32_t USART_SetModemControl (ARM_USART_MODEM_CONTROL control) {
  (void)control;

  // Manual control of modem control lines is not supported by HAL
  return ARM_DRIVER_ERROR_UNSUPPORTED;
}

/**
  \fn          ARM_USART_MODEM_STATUS USART_GetModemStatus (void)
  \brief       Get USART Modem Status lines state.
  \return      modem status \ref ARM_USART_MODEM_STATUS
*/
static ARM_USART_MODEM_STATUS USART_GetModemStatus (void) {
  ARM_USART_MODEM_STATUS modem_status;

  // Retrieving modem control lines status information is supported by HAL
  memset(&modem_status, 0, sizeof(ARM_USART_MODEM_STATUS));

  return modem_status;
}

// HAL callback functions ******************************************************

/**
  \fn          void HAL_UART_TxCpltCallback (UART_HandleTypeDef *huart)
  \brief       Tx Transfer completed callback.
  \param[in]   huart    UART handle
  */
void HAL_UART_TxCpltCallback (UART_HandleTypeDef *huart) {
  const RO_Info_t *ptr_ro_info;

  ptr_ro_info = USART_GetInfo(huart);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  ptr_ro_info->ptr_rw_info->cb_event(ARM_USART_EVENT_TX_COMPLETE | ARM_USART_EVENT_SEND_COMPLETE);
}

/**
  \fn          void HAL_UART_RxCpltCallback (UART_HandleTypeDef *huart)
  \brief       Rx Transfer completed callback.
  \param[in]   huart    UART handle
  */
void HAL_UART_RxCpltCallback (UART_HandleTypeDef *huart) {
  const RO_Info_t *ptr_ro_info;

  ptr_ro_info = USART_GetInfo(huart);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info->cb_event == NULL) {
    return;
  }

  ptr_ro_info->ptr_rw_info->cb_event(ARM_USART_EVENT_RECEIVE_COMPLETE);
}

/**
  \fn          void HAL_UART_ErrorCallback (UART_HandleTypeDef *huart)
  \brief       UART error callback.
  \param[in]   huart    UART handle
  */
void HAL_UART_ErrorCallback (UART_HandleTypeDef *huart) {
  const RO_Info_t *ptr_ro_info;
        uint32_t   error;
        uint32_t   event;

  ptr_ro_info = USART_GetInfo(huart);

  if (ptr_ro_info == NULL) {
    return;
  }
  if (ptr_ro_info->ptr_rw_info == NULL) {
    return;
  }

  error = HAL_UART_GetError(huart);
  event = 0U;

  if ((error & HAL_UART_ERROR_PE) != 0U) {
    event |= ARM_USART_EVENT_RX_PARITY_ERROR;
    ptr_ro_info->ptr_rw_info->rx_parity_error = 1U;
  }

  if ((error & HAL_UART_ERROR_FE) != 0U) {
    event |= ARM_USART_EVENT_RX_FRAMING_ERROR;
    ptr_ro_info->ptr_rw_info->rx_framing_error = 1U;
  }

  if ((error & HAL_UART_ERROR_ORE) != 0U) {
    event |= ARM_USART_EVENT_RX_OVERFLOW;
    ptr_ro_info->ptr_rw_info->rx_overflow = 1U;
  }

  if ((ptr_ro_info->ptr_rw_info->cb_event != NULL) && (event != 0U)) {
    ptr_ro_info->ptr_rw_info->cb_event(event);
  }
}

// Local driver functions definitions (for instances)
#ifdef MX_UART1
FUNCS_DEFINE(1)
#endif
#ifdef MX_UART2
FUNCS_DEFINE(2)
#endif
#ifdef MX_UART3
FUNCS_DEFINE(3)
#endif
#ifdef MX_UART4
FUNCS_DEFINE(4)
#endif
#ifdef MX_UART5
FUNCS_DEFINE(5)
#endif
#ifdef MX_UART6
FUNCS_DEFINE(6)
#endif
#ifdef MX_UART7
FUNCS_DEFINE(7)
#endif
#ifdef MX_UART8
FUNCS_DEFINE(8)
#endif
#ifdef MX_UART9
FUNCS_DEFINE(9)
#endif
#ifdef MX_UART10
FUNCS_DEFINE(10)
#endif
#ifdef MX_UART11
FUNCS_DEFINE(11)
#endif
#ifdef MX_UART12
FUNCS_DEFINE(12)
#endif
#ifdef MX_UART13
FUNCS_DEFINE(13)
#endif
#ifdef MX_UART14
FUNCS_DEFINE(14)
#endif
#ifdef MX_UART15
FUNCS_DEFINE(15)
#endif
#ifdef MX_UART21
FUNCS_DEFINE(21)
#endif
#ifdef MX_UART22
FUNCS_DEFINE(22)
#endif
#ifdef MX_UART23
FUNCS_DEFINE(23)
#endif

// Global driver structures ****************************************************

#ifdef MX_UART1
USART_DRIVER(1)
#endif
#ifdef MX_UART2
USART_DRIVER(2)
#endif
#ifdef MX_UART3
USART_DRIVER(3)
#endif
#ifdef MX_UART4
USART_DRIVER(4)
#endif
#ifdef MX_UART5
USART_DRIVER(5)
#endif
#ifdef MX_UART6
USART_DRIVER(6)
#endif
#ifdef MX_UART7
USART_DRIVER(7)
#endif
#ifdef MX_UART8
USART_DRIVER(8)
#endif
#ifdef MX_UART9
USART_DRIVER(9)
#endif
#ifdef MX_UART10
USART_DRIVER(10)
#endif
#ifdef MX_UART11
USART_DRIVER(11)
#endif
#ifdef MX_UART12
USART_DRIVER(12)
#endif
#ifdef MX_UART13
USART_DRIVER(13)
#endif
#ifdef MX_UART14
USART_DRIVER(14)
#endif
#ifdef MX_UART15
USART_DRIVER(15)
#endif
#ifdef MX_UART21
USART_DRIVER(21)
#endif
#ifdef MX_UART22
USART_DRIVER(22)
#endif
#ifdef MX_UART23
USART_DRIVER(23)
#endif

#endif  // DRIVER_CONFIG_VALID

/*! \endcond */
